package io.acna.basedao.mybatis.plugin;

import io.acna.basedao.mybatis.core.QueryCondition;
import io.acna.basedao.mybatis.core.QueryCondition.OrderByType;
import io.acna.basedao.mybatis.core.RowBound;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Invocation;

public class QueryUtil {

	private enum InterceptorType {
		COUNT, SEARCH
	}

	final static String LIKE = " LIKE ";
	final static String WHERE = " WHERE ";
	final static String IS = " IS ";
	final static String NULL = " NULL ";
	final static String ORDERBY = " ORDER BY ";
	final static String SPACE = " ";
	final static String ISNOT = " != ";

	public static Object processQuery(Invocation invocation) {
		RoutingStatementHandler handler = (RoutingStatementHandler) invocation
				.getTarget();
		StatementHandler delegate = (StatementHandler) ReflectUtil
				.getFieldValue(handler, "delegate");
		BoundSql boundSql = delegate.getBoundSql();
		// 获取参数
		Object obj = boundSql.getParameterObject();
		// 是否拦截标记
		boolean intercept = false;
		// 条件包装体对象
		QueryCondition condition = new QueryCondition();
		// 拦截类型(COUNT总数 或者 SELECT列表查询)
		InterceptorType type = null;
		// 如果包含Condition对象则重新包装SQL[COUNT]
		if (obj instanceof QueryCondition) {
			intercept = true;
			type = InterceptorType.COUNT;
			condition = (QueryCondition) obj;
		} else if (obj instanceof HashMap<?, ?>) {
			@SuppressWarnings("unchecked")
			Map<String, Object> cs = (Map<String, Object>) obj;
			for (Entry<String, Object> c : cs.entrySet()) {
				if (c.getValue() instanceof RowBound) {
					try {
						BeanUtils.populate(condition, cs);
						break;
					} catch (IllegalAccessException | InvocationTargetException e) {
						e.printStackTrace();
					}
				}
				if (c.getValue() instanceof QueryCondition)
					condition = (QueryCondition) c.getValue();
			}
			intercept = true;
			type = InterceptorType.SEARCH;
		}
		if (intercept) {// 进入SQL拦截
			MappedStatement mappedStatement = (MappedStatement) ReflectUtil
					.getFieldValue(delegate, "mappedStatement");
			Connection connection = (Connection) invocation.getArgs()[0];
			String AND = condition.getType().toString() + " ";
			boolean isSearch = false;// [依据先后顺序，标记是否进行过Search的SQL拼接]
			boolean isBetween = false;
			boolean isNot = false;
			// 获取源SQL字符串
			String sqlSource = boundSql.getSql();
			// 设置条件后的包装SQL变量
			String conditionSql = "";
			// 进入condition对象的条件拆分
			if (condition.getSearchContions() != null
					&& condition.getSearchContions().size() > 0) {
				isSearch = true;
				conditionSql += condition.getConnectFlag().getValue();
				int count = 0;
				boolean isLike = condition.getIsLike();
				for (Entry<String, Object> c : condition.getSearchContions()
						.entrySet()) {// 拆分SEARCH条件
					if (c.getValue() == null) {
						conditionSql += c.getKey() + IS + NULL;
					} else if (c.getValue() instanceof String) {// 如果是字符串类型，进一步判断是否模糊查询
						if (isLike)// 模糊查询[前后加上%]
						{
							if (c.getValue().equals("is not null")) {// 查询条件
																		// :查询的属性不为空by
																		// HYC
								conditionSql += c.getKey() + c.getValue();
							} else if (c.getValue().equals("is null")) {
								// 查询条件 :查询的属性为空by HYC
								conditionSql += c.getKey() + c.getValue();
							} else {
								String value = (String) c.getValue();
								conditionSql += c.getKey() + LIKE + "\"%"
										+ value + "%\"";
							}

						} else
							// 否则，等值查询
							conditionSql += c.getKey() + " = " + "\""
									+ c.getValue() + "\"";
					} else if (c.getValue() instanceof Integer
							|| c.getValue() instanceof Long
							|| c.getValue() instanceof Boolean) {// 数字类型直接等值[无视isLike属性]
						conditionSql += c.getKey() + " = " + c.getValue();
					}
					if (++count < condition.getSearchContions().size())// 不是最后一个条件后缀
																		// and
																		// 继续拼接
						conditionSql += " " + AND;
				}
			}
			// 进入Between条件拆分
			if (condition.getBetweenConditions() != null
					&& condition.getBetweenConditions().size() > 0) {
				if (!isSearch)
					conditionSql += condition.getConnectFlag().getValue();
				else
					conditionSql += AND;
				int count = 0;
				for (String betweenSql : condition.getBetweenConditions()) {
					conditionSql += betweenSql;
					if (++count < condition.getBetweenConditions().size())
						conditionSql += AND;
				}
			}
			// 进入IsNot拆分
			if (condition.getIsNotConditions() != null
					&& condition.getIsNotConditions().size() > 0) {
				if (!isSearch && !isNot)
					conditionSql += condition.getConnectFlag().getValue();
				else
					conditionSql += " " + AND;
				int count = 0;
				for (Entry<String, Object> c : condition.getIsNotConditions()
						.entrySet()) {
					conditionSql += c.getKey() + ISNOT + c.getValue();
					if (++count < condition.getBetweenConditions().size())
						conditionSql += AND;
				}
			}
			// 进入ORDERBY条件拆分
			if (condition.getOrderBy() != null) {// 默认为空
				String orderbyType = null;
				String columnName = null;
				for (Entry<String, OrderByType> orderby : condition
						.getOrderBy().entrySet()) {
					orderbyType = orderby.getValue().toString();
					columnName = orderby.getKey();
					break;
				}
				conditionSql += ORDERBY + columnName + SPACE + orderbyType;
			}
			if (type == InterceptorType.SEARCH) {
				// 对ORACLE分页语句的拆分[仅适用ORACLE]
				// Pattern p =
				// Pattern.compile("rownum row_id from \\(([^)]+)\\)");
				// Pattern p = Pattern.compile("(select[\\s\\S]+) limit");
				// Matcher m = p.matcher(sqlSource);
				// System.out.println(sqlSource);
				// while (m.find()) {
				String realSql = sqlSource.replace(" limit ?,?", "");
				sqlSource = sqlSource.replace(realSql, realSql + conditionSql);
				// }
			} else {

				sqlSource += conditionSql;
			}
			ReflectUtil.setFieldValue(boundSql, "sql", sqlSource);
		}
		try {
			return invocation.proceed();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private static class ReflectUtil {
		/**
		 * 利用反射获取指定对象的指定属性
		 *
		 * @param obj
		 *            目标对象
		 * @param fieldName
		 *            目标属性
		 * @return 目标属性的值
		 */
		public static Object getFieldValue(Object obj, String fieldName) {
			Object result = null;
			Field field = ReflectUtil.getField(obj, fieldName);
			if (field != null) {
				field.setAccessible(true);
				try {
					result = field.get(obj);
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return result;
		}

		/**
		 * 利用反射获取指定对象里面的指定属性
		 *
		 * @param obj
		 *            目标对象
		 * @param fieldName
		 *            目标属性
		 * @return 目标字段
		 */
		private static Field getField(Object obj, String fieldName) {
			Field field = null;
			for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz
					.getSuperclass()) {
				try {
					field = clazz.getDeclaredField(fieldName);
					break;
				} catch (NoSuchFieldException e) {
					// 这里不用做处理，子类没有该字段可能对应的父类有，都没有就返回null。
				}
			}
			return field;
		}

		/**
		 * 利用反射设置指定对象的指定属性为指定的值
		 *
		 * @param obj
		 *            目标对象
		 * @param fieldName
		 *            目标属性
		 * @param fieldValue
		 *            目标值
		 */
		public static void setFieldValue(Object obj, String fieldName,
				String fieldValue) {
			Field field = ReflectUtil.getField(obj, fieldName);
			if (field != null) {
				try {
					field.setAccessible(true);
					field.set(obj, fieldValue);
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public static QueryCondition createCondition() {
		return new QueryCondition();
	}

	public static QueryCondition createConditionQueryAll() {
		QueryCondition c = createCondition();
		c.isQueryAll();
		return c;
	}

	public static QueryCondition createCondition(String name, Object value) {
		QueryCondition c = createCondition();
		c.addSearchContions(name, value);
		return c;
	}

	public static QueryCondition createCondition(int pagNum, int rows) {
		QueryCondition c = createCondition();
		c.setPage(pagNum);
		c.setRows(rows);
		return c;
	}
}